#!/usr/bin/env python
#
# lfstr.py - library module for pyinsim
#
# Copyright 2008-2012 Alex McBride <xandermcbride@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU Lesser General Public License version 3 or any later version.
#

__all__ = ('lfsencode', 'lfsdecode',)

# Constants.
_ENCODING_MAP = {ord('L'): 'cp1252', ord('E'): 'cp1250', ord('T'): 'cp1254', ord('B'): 'cp1257',
                ord('C'): 'cp1251', ord('G'): 'cp1253', ord('J'): 'cp932',  ord('H'): 'cp950', 
                ord('S'): 'cp936', ord('K'): 'cp949',}
_ESCAPE_MAP = {ord('v'): '|', ord('a'): '*', ord('c'): ':', ord('d'): '\\', ord('s'): '/',
              ord('q'): '?', ord('t'): '"', ord('l'): '<', ord('r'): '>', ord('^'): '^',}
_DEFAULT_ENCODING = 'cp1252'
_CHAR_NULL = 0
_CHAR_ESCAPE = ord('^')
_ENCODE_FALLBACK = b'?'


# Functions.
def lfsdecode(lfstr):
    """Decode a LFS encoded string to unicode."""
    if not lfstr:
        return ''
    encoding = _DEFAULT_ENCODING
    output = ''
    i = last_escape = 0
    while i < len(lfstr):
        if lfstr[i] == _CHAR_NULL:
            break
        elif lfstr[i]== _CHAR_ESCAPE:
            if i - last_escape:
                output += lfstr[last_escape:i].decode(encoding, errors='replace')
            last_escape = i + 2
            
            cp = _ENCODING_MAP.get(lfstr[i+1])
            if cp:
                encoding = cp
            else:
                esc = _ESCAPE_MAP.get(lfstr[i+1])
                if esc:
                    output += esc
                else:
                    output += chr(lfstr[i]) + chr(lfstr[i+1])
            i += 1                
        i += 1
    if i - last_escape:
        output += lfstr[last_escape:i].decode(encoding)  
    return output


def lfsencode(unistr):
    """Encode a unicode to a LFS encoded string."""
    if not unistr:
        return b''
    encoding = _DEFAULT_ENCODING
    output = b''
    for c in unistr:
        try:
            output += c.encode(encoding, errors='strict')
        except UnicodeEncodeError:
            found = False
            for esc, cp in _ENCODING_MAP.items():
                if cp != encoding:
                    try:
                        output += b'^' + bytes([esc]) + c.encode(cp, errors='strict')
                    except UnicodeEncodeError:
                        pass
                    else:
                        encoding = cp
                        found = True
                        break
            if not found:
                output += _ENCODE_FALLBACK
    return output

